using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Godot;

namespace Arcane.Source.Scripting
{
    public abstract partial class Ast
    {
        public Ast this[Variant index]
        {
            get => this switch
            {
                Compound c => index.VariantType switch
                {
                    Variant.Type.Int => c.asts[index.AsInt32()],
                    Variant.Type.String => c.asts
                        .Where(ast => ast is KVPair kv && kv.key is Ident id && id.name == index.AsString())
                        .Select(ast => (ast as KVPair).value)
                        .First(),
                    _ => null,
                },
                _ => null,
            };
        }

        public static Value Val(Variant val) => new(val);
        public static Ident Id(string name) => new() { name = name };
        public static KVPair Kv(Ast key, Ast val) => new() { key = key, value = val };
        public static Quoted Quote(Ast ast) => new() { ast = ast };
        public static Compound Comp(params Ast[] asts) => new() { asts = asts };

        public static Ast FromVariant(Variant v)
        {
            switch (v.VariantType)
            {
                case Variant.Type.Array:
                    var arr = v.AsGodotArray();
                    if (arr[0].VariantType == Variant.Type.StringName && arr[0].AsStringName() == Quoted.QUOTE)
                    {
                        return Quote(FromVariant(arr[1]));
                    }
                    return Comp(arr.Select(FromVariant).ToArray());
                case Variant.Type.StringName:
                    return Id(v.AsString());
                default:
                    return Val(v);
            }
        }

        public abstract Variant ToVariant();
    }

    public class Value(Variant val) : Ast
    {
        public Variant value = val;

        public override Variant ToVariant() => value;
        public override string ToString() => value.VariantType switch
        {
            Variant.Type.Nil => "nil",
            Variant.Type.String => $"\"{value}\"",
            _ => value.ToString(),
        };

        public Value Neg() => value.VariantType switch
        {
            Variant.Type.Int => new(-value.AsInt64()),
            Variant.Type.Float => new(-value.AsDouble()),
            _ => new(value),
        };
    }

    public class Ident : Ast
    {
        public string name;
        public override string ToString() => name;
        public override Variant ToVariant() => new StringName(name);
    }

    public class KVPair : Ast
    {
        public Ast key;
        public Ast value;

        public override string ToString() => key is Ident id ? $"{id}:{value}" : $":{key} {value}";
        public override Variant ToVariant() => value.ToVariant();
    }

    public class Quoted : Ast
    {
        public static readonly StringName QUOTE = "'";
        public Ast ast;
        public override string ToString() => $"'{ast}";
        public override Variant ToVariant() => new Godot.Collections.Array([QUOTE, ast.ToVariant()]);
    }

    public class Compound : Ast
    {
        public Ast[] asts;

        public string FuncCallName => asts[0] is Ident id ? id.name : null;
        public IEnumerable<KVPair> KVPairs => asts.Where(ast => ast is KVPair).Cast<KVPair>();

        public override string ToString() => $"({string.Join(' ', asts.Select(p => p.ToString()))})";
        public override Variant ToVariant() => new Godot.Collections.Array(asts.Select(ast => ast.ToVariant()));
    }
}